﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PerCederberg.Grammatica.Runtime;

namespace SafeScript.MetaModel.Statements
{
    public class StatementList : List<Statement>
    {
    }

    public abstract class Statement
    {
        public Node Node;

        public virtual void ComputeType(ErrorReport report)
        {
            // TODO: Make this one abstract
        }

        protected void ReportNotAnObject(Node node, ErrorReport report)
        {
            report.Add(new ErrorEntry(node) { Type = ErrorType.NotAnObject });
        }

        protected InstanceType ToInstance(ref Operator op, ITypeDef opType, ErrorReport report)
        {
            if (opType is InstanceType)
                return opType as InstanceType;
            if (opType is FunctionTypeDef)
            {
                InstanceType t = new InstanceType() { Type = opType };
                op = new FunctionObjectOperator() { Function = op, Node = op.Node };
                return t;
            }
            this.ReportNotAnObject(op.Node, report);
            return null;
        }

        protected ITypeDef ConvertTo(ref Operator op, ITypeDef isType, ITypeDef goalType, ErrorReport report)
        {
            ITypeDef g = goalType is InstanceType ? (goalType as InstanceType).Type : goalType;
            ITypeDef i = isType is InstanceType ? (isType as InstanceType).Type : isType;

            // We can convert a function into a binding
            if (g is BindingTypeDef && i is FunctionTypeDef)
            {
                if (!BindingTypeDef.IsEqualTo(i as FunctionTypeDef, g as BindingTypeDef))
                {
                    this.ReportIncompatibleTypes(g, i, report);
                    return null;
                }
                op = new BindingObjectOperator() { Node = op.Node, Operator = op, Type = goalType as BindingTypeDef };
                return g;
            }

            if ( !i.CanBeAssignedTo(g) )
            {
                this.ReportIncompatibleTypes(g, i, report);
                return null;
            }
            return i;
        }

        protected void ReportIncompatibleTypes(ITypeDef type1, ITypeDef type2, ErrorReport report)
        {
            report.Add(new ErrorEntry(this.Node) { Type = ErrorType.IncompatibleTypes });
        }
    }
}
